﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Linq;

namespace LegtionMVVM
{
    /// <summary>
    /// View 和 View Model 缓存；
    /// 一切的view 和 view model绑定从这里开始。
    /// </summary>
    internal class Document
    {
        private static Dictionary<object, object> documentCache = new Dictionary<object, object>();
        public static TView CreateDocument<TView, TViewModel>(out TViewModel viewModel)
            where TView : Form
            where TViewModel : NotifyPropertyChanged
        {
            TView view = null;
            viewModel = PropertyAop.Create<TViewModel>();
            view = (TView)Activator.CreateInstance(typeof(TView));
            view.Disposed += viewModel.ViewDisposed;

            if (viewModel == null)
            {
                throw new Exception(typeof(TView).FullName + "\n" + "  没有默认无参数的公共构造函数！");
            }
            if (view == null)
            {
                throw new Exception(typeof(TViewModel).FullName + "\n" + "  没有默认无参数的公共构造函数！");
            }
            //先处理绑定
            SourceManager<TView, TViewModel> source = new SourceManager<TView, TViewModel>(view, viewModel);
            source.Scan();

            documentCache.Add(view, viewModel);

            return view;
        }
        public static TView GetView<TView, TViewModel>(TViewModel viewModel)
            where TView : Form
            where TViewModel : NotifyPropertyChanged
        {
            foreach (KeyValuePair<object,object> item in documentCache)
            {
                if (viewModel.Equals((TViewModel)item.Value))
                    return (TView)item.Key;
            }
            return null;
        }
        public static TViewModel GetViewModel<TView, TViewModel>(TView view)
            where TView : Form
            where TViewModel : NotifyPropertyChanged
        {
            if (documentCache.ContainsKey(view))
                return (TViewModel)documentCache[view];
            return null;
        }
        public static void CloseDocument<TView, TViewModel>(TViewModel viewModel,bool closeView=true)
            where TView : Form
            where TViewModel : NotifyPropertyChanged
        {
            TView view= GetView<TView, TViewModel>(viewModel);
            if (view != null)
            {
                documentCache.Remove(view);
                if (closeView)
                {
                    view.Close(); 
                }                               
                view = null;
            }
        }
    }
    internal class SourceManager<TView, TViewModel>
        where TView : Form
        where TViewModel : NotifyPropertyChanged
    {
        internal List<ToBindControl> bindControlList = new List<ToBindControl>();
 
        internal TView view = null;
        internal TViewModel viewModel = null;
        internal LegtionMVVMBinder binder = null;
        public SourceManager(TView view, TViewModel viewModel)
        {
            this.view = view;
            this.viewModel = viewModel;
            binder = GetBinder;
        }
        public void Scan()
        {
            if (view != null && viewModel != null)
            {
                ResolvingBindingScript();
                BindingManager.BindData<TView,TViewModel>(bindControlList,view,viewModel);
            }
        }
        internal LegtionMVVMBinder GetBinder
        {
            get
            {
                if(binder==null&&view!=null)
                {
                    FieldInfo[] fis = typeof(TView).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
                    string binderFullName = typeof(LegtionMVVMBinder).FullName;
                    if (fis != null && fis.Length > 0)
                    {
                        foreach (FieldInfo fi in fis)
                        {
                            if (fi.FieldType.FullName == binderFullName)
                            {
                                binder = fi.GetValue(view) as LegtionMVVMBinder;
                                break;
                            }
                        }
                    }
                }
                return binder;
            }          
        }
        private void ResolvingBindingScript()
        {
            if (GetBinder.bindingList.Count > 0)
            {
                foreach (KeyValuePair<Control,string> item in GetBinder.bindingList)
                {
                    BindingScript.ResolvingBindingScript<TView,TViewModel>(this, item.Key, item.Value );
                }
            }
        }
        private void ResolvingCommandScript()
        {
            if (GetBinder.commandClassList.Count > 0)
            {
                foreach (KeyValuePair<Control, string> item in GetBinder.commandClassList)
                {
                    BindingScript.ResolvingBindingScript<TView, TViewModel>(this, item.Key, item.Value);
                }
            }
        }
    }

    internal class Source
    {

        #region ControlTypeProperties
        static Dictionary<string, Dictionary<string, PropertyInfo>> typeProperties = new Dictionary<string, Dictionary<string, PropertyInfo>>();
        static Dictionary<string, PropertyInfo[]> typePropertyArray = new Dictionary<string, PropertyInfo[]>();
        public static PropertyInfo[] GetTypeProperties(Type type = null, string fullName = "")
        {
            if (type == null && string.IsNullOrEmpty(fullName))
            {
                return null;
            }
            fullName = type == null ? fullName : type.FullName;
            if (!typePropertyArray.ContainsKey(fullName))
            {
                if (type != null)
                {
                    PropertyInfo[] properties = type.GetProperties();
                    typePropertyArray.Add(fullName, properties);
                    if (!typeProperties.ContainsKey(fullName))
                    {
                        typeProperties.Add(fullName, new Dictionary<string, PropertyInfo>());
                        foreach (PropertyInfo p in properties)
                        {
                            if (!typeProperties[fullName].ContainsKey(p.Name) && p.DeclaringType.FullName == type.FullName)
                                typeProperties[fullName].Add(p.Name, p);
                        }
                    }

                    return properties;
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return typePropertyArray[fullName];
            }
        }
        public static PropertyInfo GetProperty(Type type, string propertyName)
        {
            if (type == null || string.IsNullOrEmpty(propertyName))
                return null;
            string fullName = type.FullName;
            if (!typeProperties.ContainsKey(fullName))
                GetTypeProperties(type, fullName);
            if (typeProperties.ContainsKey(fullName))
                if (typeProperties[fullName].ContainsKey(propertyName))
                    return typeProperties[fullName][propertyName];
            return null;
        }
        #endregion
        #region ControlTypePropertiesName
        static Dictionary<string, List<string>> typePropertiesName = new Dictionary<string, List<string>>();
        public static List<string> GetTypePropertiesName(Type type = null, string fullName = "")
        {
            if (type == null && string.IsNullOrEmpty(fullName))
            {
                return null;
            }

            fullName = type == null ? fullName : type.FullName;
            List<string> listPropertyName = null;
            if (!typePropertiesName.ContainsKey(fullName))
            {
                if (type != null)
                {
                    listPropertyName = new List<string>();
                    PropertyInfo[] propertyies = GetTypeProperties(type, fullName);
                    foreach (PropertyInfo p in propertyies)
                    {
                        listPropertyName.Add(p.Name);
                    }
                    listPropertyName.Sort();

                    Action<string> exec = delegate(string str)
                    {
                        if (listPropertyName.Contains(str))
                        {
                            listPropertyName.Remove(str);
                            listPropertyName.Insert(0, str);
                        }
                    };
                    exec("Value");
                    exec("Text");
                    typePropertiesName.Add(fullName, listPropertyName);
                }
                else
                {
                    listPropertyName = typePropertiesName[fullName];
                }
            }
            else
            {
                listPropertyName = typePropertiesName[fullName];
            }
            return listPropertyName;
        }
        #endregion

        #region Type
        static Dictionary<string, TypeSource> allUseTypeSources = new Dictionary<string, TypeSource>();
        public static void SetTypeSource(Type type)
        {
            if (type == null)
                return;
            string fullName = type.FullName;
            if (allUseTypeSources.ContainsKey(fullName))
                return;
            if (type.IsValueType || type.FullName.ToLower() == "System.String".ToLower()
                || type.FullName.ToLower() == "System.Object".ToLower() || !type.IsClass)//不处理值类型 和string object类型
                return;
            TypeSource ts = new TypeSource
            {
                Type = type
            };
            allUseTypeSources.Add(fullName, ts);
        }
        internal static TypeSource GetTypeSource(string fullTypeName)
        {
            return allUseTypeSources.ContainsKey(fullTypeName) ? allUseTypeSources[fullTypeName] : null;
        }
        /// <summary>
        /// 可以获取到相应的 TypeSource,如果没有就设置和返回。
        /// </summary>
        /// <param name="type"></param>
        /// <param name="PropertyKeyName"></param>
        /// <returns></returns>
        internal static TypeSource GetTypeSource(Type type)
        {
            if (type == null)
                return null;
            SetTypeSource(type);
            return GetTypeSource(type.FullName);
        }
        #endregion
        #region BindingList

        #endregion

        #region EventsName
        static Dictionary<string, List<string>> typeEventsName = new Dictionary<string, List<string>>();
        public static List<string> GetTypeEventsName(Type type = null, string fullName = "")
        {
            if (type == null && string.IsNullOrEmpty(fullName))
            {
                return null;
            }

            fullName = type == null ? fullName : type.FullName;
            List<string> listEventsName = null;
            if (!typeEventsName.ContainsKey(fullName))
            {
                if (type != null)
                {
                    Action<string> exec = delegate(string str)
              {
                  if (listEventsName.Contains(str))
                  {
                      listEventsName.Remove(str);
                      listEventsName.Insert(0, str);
                  }
              };
                    listEventsName = new List<string>();
                    EventInfo[] events = type.GetEvents();
                    foreach (EventInfo e in events)
                    {
                        listEventsName.Add(e.Name);
                        if (e.Name.Contains("Select") || e.Name.Contains("Click"))
                            exec(e.Name);
                    }
                    //listEventsName.Sort();

                    exec("Select");
                    exec("Click");
                    typeEventsName.Add(fullName, listEventsName);                    
                }
                else
                {
                    listEventsName = typeEventsName[fullName];
                }
            }
            else
            {
                listEventsName = typeEventsName[fullName];
            }
            return listEventsName;
        }
        #endregion
    }
 
    class ToBindControl
    {
        public Control Component { get; set; }
        public string AllScript { get; set; }
        Type componentType = null;
        public Type Type
        {
            get
            {
                if (componentType == null)
                    if (Component != null)
                        componentType = Component.GetType();
                return componentType;
            }
        }

        EventInfo[] events = null;
        public EventInfo[] Events
        {
            get
            {
                if (Component != null && events == null)
                    events = Type.GetEvents();
                return events;
            }
        }
        
        /// <summary>
        /// 处理绑定数据的脚本
        /// </summary>
        public Dictionary<string, string> BindingDataScriptList = new Dictionary<string, string>();
        public Dictionary<string, BindSource> BindingSourceList = new Dictionary<string, BindSource>();
    }
    class BindSource
    {
        public string FullPath{ get; set; }
        public string PropertyKey{ get; set; }
        public PropertyInfo SrcProperty { get; set; }
        public BindingMode BindingMode { get; set; }
        public void SetBindingMode(char modeCh)
        {
            BindingMode = BindingMode.TwoWay;
            switch (modeCh)
            {
                case '=': BindingMode = BindingMode.TwoWay;
                    break;
                case '>': BindingMode = BindingMode.OneWayC2D; 
                    break;
                case '<': BindingMode = BindingMode.OneWayD2C;
                    break;
            }
        }
 
        public Dictionary<string, TypeSource> LayerSourcePath = new Dictionary<string, TypeSource>();
        /// <summary>
        /// 目标属性
        /// </summary>
        public PropertyInfo DesProperty { get; set; }
    }
    class CommandSource
    {
        public MethodInfo GetCommandMethod { get; set; }
    }
    enum BindingMode
    {
        [Description(">")]
        OneWayC2D=10,
         [Description("<")]
        OneWayD2C=20,
        [Description("=")]
        TwoWay=30
    }
 
    internal class TypeSource
    {
        public Type Type { get; set; }
        public string FullTypeName { get { return Type.FullName; } }

        Dictionary<string, string> _propertyNameToTypeFullName = new Dictionary<string, string>();
        public Dictionary<string, string> PropertyNameToTypeFullName
        {
            get
            {
                if (_propertyNameToTypeFullName.Count == 0 && Type!=null)
                {
                    PropertyInfo[] propertyies = Type.GetProperties();
                    foreach (PropertyInfo p in propertyies)
                    {
                        if (!_propertyNameToTypeFullName.ContainsKey(p.Name))
                        {
                            _propertyNameToTypeFullName.Add(p.Name, p.PropertyType.FullName);
                        }
                    }
                }
                return _propertyNameToTypeFullName;
            }
        }

        public List<string> PropertiesName
        {
            get
            {
                return Source.GetTypePropertiesName(Type, FullTypeName);
            }
        }
    }

 
}
